﻿using System;
using System.Globalization;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using System.ComponentModel.DataAnnotations;

namespace Gmantis.Controls.DataGrid
{
    public class DataGridBoundColumn : DataGridColumn
    {
        // Fields
        private string _format;
        public static readonly DependencyProperty AllowNullProperty = DependencyProperty.Register("AllowNull", typeof(bool), typeof(DataGridBoundColumn), new PropertyMetadata(false));
        public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register("TextTrimming", typeof(TextTrimming), typeof(DataGridBoundColumn), new PropertyMetadata(TextTrimming.None, new PropertyChangedCallback(DataGridBoundColumn.Refresh)));
        public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(DataGridBoundColumn), new PropertyMetadata(TextWrapping.NoWrap, new PropertyChangedCallback(DataGridBoundColumn.Refresh)));

        // Methods
        public DataGridBoundColumn()
        {
            this._format = "";
            this.Initialize();
        }

        public DataGridBoundColumn(PropertyInfo property)
            : this()
        {
            this.Initialize();
            Binding binding = new Binding(property.Name)
            {
                ValidatesOnExceptions = true,
                ValidatesOnDataErrors = true,
                ValidatesOnNotifyDataErrors = true,
                UpdateSourceTrigger = UpdateSourceTrigger.Default
            };
            this.Binding = binding;
            base.IsReadOnly = !property.CanWrite;
            base.Header = property.Name;
            base.CanUserSort = typeof(IComparable).IsAssignableFrom(property.PropertyType.GetNonNullableType());
            this.AllowNull = property.PropertyType.IsNullableType();
            this.SetCustomAttributes(property);
        }

        public override bool BeginEdit(FrameworkElement editingElement, RoutedEventArgs routedEventArgs)
        {
            return base.ActualIsEditable;
        }

        public override void BindCellContent(FrameworkElement cellContent, DataGridRow row)
        {
            TextBlock block = (TextBlock)cellContent;
            block.HorizontalAlignment = base.HorizontalAlignment;
            block.VerticalAlignment = base.VerticalAlignment;
            block.TextWrapping = this.TextWrapping;
            block.TextTrimming = this.TextTrimming;
            if (base.CellContentStyle != null)
            {
                block.Style = base.CellContentStyle;
            }
            else
            {
                block.Margin = new Thickness(4.0, 4.0, 4.0, 4.0);
            }
            if (this.Binding != null)
            {
                Binding binding = this.CopyBinding(this.Binding, (base.DataGrid != null) ? base.DataGrid.Language : null);
                binding.Source = row.DataItem ?? "";
                binding.Mode = BindingMode.OneWay;
                block.SetBinding(TextBlock.TextProperty, binding);
            }
            else
            {
                block.ClearValue(TextBlock.TextProperty);
            }
        }

        public override void CancelCellEdit(FrameworkElement editingElement, object uneditedValue)
        {
        }

        protected internal Binding CopyBinding(Binding binding, XmlLanguage lang = null)
        {
            Binding binding2;
            if (binding.Path == null)
            {
                binding2 = new Binding();
            }
            else
            {
                binding2 = new Binding(binding.Path.Path);
            }
            if (base.ActualIsEditable)
            {
                binding2.Mode = BindingMode.TwoWay;
            }
            else
            {
                binding2.Mode = BindingMode.OneWay;
            }
            binding2.Converter = binding.Converter ?? this.DefaultConverter;
            binding2.ConverterCulture = binding.ConverterCulture ?? DataGridColumn.GetEquivalentCulture(lang);
            binding2.ConverterParameter = binding.ConverterParameter ?? this.GetActualFormat();
            binding2.NotifyOnValidationError = binding.NotifyOnValidationError;
            if (binding.Source != null)
            {
                binding2.Source = binding.Source;
            }
            binding2.ValidatesOnExceptions = binding.ValidatesOnExceptions;
            binding2.ValidatesOnDataErrors = binding.ValidatesOnDataErrors;
            binding2.ValidatesOnNotifyDataErrors = binding.ValidatesOnNotifyDataErrors;
            binding2.UpdateSourceTrigger = binding.UpdateSourceTrigger;
            return binding2;
        }

        public override FrameworkElement CreateCellContent(DataGridRow row)
        {
            return new TextBlock();
        }

        public override FrameworkElement CreateGroupContent()
        {
            TextBlock block = new TextBlock
            {
                TextTrimming = TextTrimming.WordEllipsis,
                VerticalAlignment = VerticalAlignment.Center
            };
            Binding binding = new Binding
            {
                TargetNullValue = "",
                Converter = this.GroupContentConverter,
                ConverterParameter = this.GetActualFormat(),
                ConverterCulture = this.GetActualCulture()
            };
            block.SetBinding(TextBlock.TextProperty, binding);
            if (base.GroupContentStyle != null)
            {
                block.Style = base.GroupContentStyle;
                return block;
            }
            block.Margin = new Thickness(4.0);
            return block;
        }

        public override void EndEdit(FrameworkElement editingElement)
        {
        }

        public virtual CultureInfo GetActualCulture()
        {
            if ((this.Binding != null) && (this.Binding.ConverterCulture != null))
            {
                return this.Binding.ConverterCulture;
            }
            return DataGridColumn.GetEquivalentCulture(base.DataGrid.Language);
        }

        public virtual string GetActualFormat()
        {
            if ((this.Binding != null) && (this.Binding.StringFormat != null))
            {
                return this.Binding.StringFormat;
            }
            return this.Format;
        }

        private T GetAttribute<T>(PropertyInfo property)
        {
            if (property != null)
            {
                object[] customAttributes = property.GetCustomAttributes(typeof(T), true);
                if (customAttributes.Length > 0)
                {
                    return (T)customAttributes[0];
                }
            }
            return default(T);
        }

        public override object GetCellContentRecyclingKey(DataGridRow row)
        {
            return typeof(TextBlock);
        }

        public override FrameworkElement GetCellEditingContent(DataGridRow row)
        {
            return null;
        }

        public override object GetCellValue(DataGridRow Row)
        {
            return Row.DataItem.GetPropertyValue<object>(this.Binding.Path.Path);
        }

        public override string GetGroupText(DataGridGroupRow groupRow)
        {
            return base.GetGroupText(groupRow);
        }

        private void Initialize()
        {
            this.GroupContentConverter = this.DefaultConverter;
        }

        public override object PrepareCellForEdit(FrameworkElement editingElement)
        {
            return null;
        }

        private static void Refresh(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as DataGridBoundColumn).Refresh();
        }

        public override void SetCellValue(DataGridRow Row, object value)
        {
            Row.DataItem.SetPropertyValue<object>(this.Binding.Path.Path, value, this.DefaultConverter, this.Format, this.GetActualCulture());
        }

        private void SetCustomAttributes(PropertyInfo property)
        {
            DisplayAttribute attribute = this.GetAttribute<DisplayAttribute>(property);
            if (attribute != null)
            {
                if (attribute.GetAutoGenerateFilter().HasValue && !attribute.GetAutoGenerateFilter().Value)
                {
                    base.CanUserFilter = false;
                }
                if (!string.IsNullOrEmpty(attribute.GetGroupName()))
                {
                    base.GroupHeader = attribute.GroupName;
                }
                if (!string.IsNullOrEmpty(attribute.GetShortName()))
                {
                    base.Header = attribute.GetShortName();
                }
            }
            DisplayFormatAttribute attribute2 = this.GetAttribute<DisplayFormatAttribute>(property);
            if ((attribute2 != null) && !string.IsNullOrEmpty(attribute2.DataFormatString))
            {
                this.Format = attribute2.DataFormatString;
            }
            EditableAttribute attribute3 = this.GetAttribute<EditableAttribute>(property);
            if (attribute3 != null)
            {
                base.IsReadOnly = !attribute3.AllowEdit;
            }
        }

        public override void UnbindCellContent(FrameworkElement cellContent, DataGridRow row)
        {
            cellContent.InvalidateMeasure();
        }

        // Properties
        public override string ActualFilterMemberPath
        {
            get
            {
                if (!string.IsNullOrEmpty(base.ActualFilterMemberPath))
                {
                    return base.ActualFilterMemberPath;
                }
                return this.BindingPath;
            }
        }

        public override string ActualSortMemberPath
        {
            get
            {
                if (!string.IsNullOrEmpty(base.ActualSortMemberPath))
                {
                    return base.ActualSortMemberPath;
                }
                return this.BindingPath;
            }
        }

        public bool AllowNull
        {
            get
            {
                return (bool)base.GetValue(AllowNullProperty);
            }
            set
            {
                base.SetValue(AllowNullProperty, value);
            }
        }

        public Binding Binding { get; set; }

        private string BindingPath
        {
            get
            {
                if (((this.Binding != null) && (this.Binding.Path != null)) && !string.IsNullOrEmpty(this.Binding.Path.Path))
                {
                    return this.Binding.Path.Path;
                }
                return null;
            }
        }

        public virtual IValueConverter DefaultConverter
        {
            get
            {
                return new DataGridBoundConverter();
            }
        }

        public virtual string Format
        {
            get
            {
                return this._format;
            }
            set
            {
                this._format = value;
                base.OnPropertyChanged("Format", true);
            }
        }

        public override string Name
        {
            get
            {
                if (!string.IsNullOrEmpty(base.Name))
                {
                    return base.Name;
                }
                return this.BindingPath;
            }
            set
            {
                base.Name = value;
            }
        }

        public TextTrimming TextTrimming
        {
            get
            {
                return (TextTrimming)base.GetValue(TextTrimmingProperty);
            }
            set
            {
                base.SetValue(TextTrimmingProperty, value);
            }
        }

        public TextWrapping TextWrapping
        {
            get
            {
                return (TextWrapping)base.GetValue(TextWrappingProperty);
            }
            set
            {
                base.SetValue(TextWrappingProperty, value);
            }
        }
    }
}
